/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on 23/07/2005
*/
package com.python.pydev.analysis.builder;
import java.io.File;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.builder.PyDevBuilderVisitor;
import org.python.pydev.core.IModule;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.callbacks.ICallback0;
import org.python.pydev.core.concurrency.RunnableAsJobsPoolThread;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.revisited.PyCodeCompletionVisitor;
import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule;
import org.python.pydev.logging.DebugSettings;
import org.python.pydev.parser.fastparser.FastDefinitionsParser;
import org.python.pydev.plugin.nature.PythonNature;
import com.aptana.shared_core.callbacks.ICallback;
import com.python.pydev.analysis.additionalinfo.AbstractAdditionalDependencyInfo;
import com.python.pydev.analysis.additionalinfo.AdditionalProjectInterpreterInfo;
public class AnalysisBuilderVisitor extends PyDevBuilderVisitor {
@Override
protected int getPriority() {
return PyCodeCompletionVisitor.PRIORITY_CODE_COMPLETION + 1; //just after the code-completion priority
}
@Override
public void visitChangedResource(final IResource resource, final ICallback0<IDocument> document,
final IProgressMonitor monitor) {
visitChangedResource(resource, document, monitor, false);
}
public void visitChangedResource(final IResource resource, final ICallback0<IDocument> document,
final IProgressMonitor monitor, boolean forceAnalysis) {
//we may need to 'force' the analysis when a module is renamed, because the first message we receive is
//a 'delete' and after that an 'add' -- which is later mapped to this method, so, if we don't have info
//on the module we should analyze it because it is 'probably' a rename.
final PythonNature nature = getPythonNature(resource);
if (nature == null) {
return;
}
//Put things from the memo to final variables as we might need them later on and we cannot get them from
//the memo later.
final String moduleName;
final SourceModule module;
final IDocument doc;
try {
doc = document.call();
if (doc == null) {
return;
}
moduleName = getModuleName(resource, nature);
module = getSourceModule(resource, doc, nature);
} catch (MisconfigurationException e) {
Log.log(e);
return;
}
//depending on the level of analysis we have to do, we'll decide whether we want
//to make the full parse (slower) or the definitions parse (faster but only with info
//related to the definitions)
ICallback<IModule, Integer> moduleCallback = new ICallback<IModule, Integer>() {
public IModule call(Integer arg) {
//Note: we cannot get anything from the memo at this point because it'll be called later on from a thread
//and the memo might have changed already (E.g: moduleName and module)
if (arg == IAnalysisBuilderRunnable.FULL_MODULE) {
if (module != null) {
return module;
} else {
try {
return createSoureModule(resource, doc, moduleName);
} catch (MisconfigurationException e) {
throw new RuntimeException(e);
}
}
} else if (arg == IAnalysisBuilderRunnable.DEFINITIONS_MODULE) {
if (DebugSettings.DEBUG_ANALYSIS_REQUESTS) {
Log.toLogFile(this, "PyDevBuilderPrefPage.getAnalyzeOnlyActiveEditor()");
}
IFile f = (IFile) resource;
String file = f.getRawLocation().toOSString();
return new SourceModule(moduleName, new File(file), FastDefinitionsParser.parse(doc.get(),
moduleName), null);
} else {
throw new RuntimeException("Unexpected parameter: " + arg);
}
}
};
long documentTime = this.getDocumentTime();
if (documentTime == -1) {
Log.log("Warning: The document time in the visitor is -1. Changing for current time.");
documentTime = System.currentTimeMillis();
}
doVisitChangedResource(nature, resource, doc, moduleCallback, null, monitor, forceAnalysis,
AnalysisBuilderRunnable.ANALYSIS_CAUSE_BUILDER, documentTime);
}
/**
* here we have to detect errors / warnings from the code analysis
* Either the module callback or the module must be set.
*/
public void doVisitChangedResource(IPythonNature nature, IResource resource, IDocument document,
ICallback<IModule, Integer> moduleCallback, final IModule module, IProgressMonitor monitor,
boolean forceAnalysis, int analysisCause, long documentTime) {
if (DebugSettings.DEBUG_ANALYSIS_REQUESTS) {
if (analysisCause == AnalysisBuilderRunnable.ANALYSIS_CAUSE_BUILDER) {
System.out.println("doVisitChangedResource: BUILDER -- " + documentTime);
} else {
System.out.println("doVisitChangedResource: PARSER -- " + documentTime);
}
}
if (module != null) {
if (moduleCallback != null) {
Log.log("Only the module or the moduleCallback must be specified for: " + resource);
return;
}
setModuleInCache(resource, module);
moduleCallback = new ICallback<IModule, Integer>() {
public IModule call(Integer arg) {
return module;
}
};
} else {
//don't set module in the cache if we only have the callback
//moduleCallback is already defined
if (moduleCallback == null) {
Log.log("Either the module or the moduleCallback must be specified for: " + resource);
return;
}
}
String moduleName;
try {
moduleName = getModuleName(resource, nature);
} catch (MisconfigurationException e) {
Log.log(e);
return;
}
final IAnalysisBuilderRunnable runnable = AnalysisBuilderRunnableFactory.createRunnable(document, resource,
moduleCallback, isFullBuild(), moduleName, forceAnalysis, analysisCause, nature, documentTime,
resource.getModificationStamp());
if (runnable == null) {
//It may be null if the document version of the new one is lower than one already active.
return;
}
execRunnable(moduleName, runnable);
}
/**
* Depending on whether we're in a full build or delta build, this method will run the runnable directly
* or schedule it as a job.
*/
private void execRunnable(final String moduleName, final IAnalysisBuilderRunnable runnable) {
if (isFullBuild()) {
runnable.run();
} else {
RunnableAsJobsPoolThread.getSingleton().scheduleToRun(runnable, "PyDev: Code Analysis:" + moduleName);
}
}
@Override
public void visitRemovedResource(IResource resource, ICallback0<IDocument> document, IProgressMonitor monitor) {
PythonNature nature = getPythonNature(resource);
if (nature == null) {
return;
}
if (resource.getType() == IResource.FOLDER) {
//We don't need to explicitly treat any folder (just its children -- such as __init__ and submodules)
return;
}
if (!isFullBuild()) {
//on a full build, it'll already remove all the info
String moduleName;
try {
moduleName = getModuleName(resource, nature);
} catch (MisconfigurationException e) {
Log.log(e);
return;
}
long documentTime = this.getDocumentTime();
if (documentTime == -1) {
Log.log("Warning: The document time in the visitor for remove is -1. Changing for current time. "
+ "Resource: " + resource + ". Module name: " + moduleName);
documentTime = System.currentTimeMillis();
}
long resourceModificationStamp = resource.getModificationStamp();
final IAnalysisBuilderRunnable runnable = AnalysisBuilderRunnableFactory.createRunnable(moduleName, nature,
isFullBuild(), false, AnalysisBuilderRunnable.ANALYSIS_CAUSE_BUILDER, documentTime,
resourceModificationStamp);
if (runnable == null) {
//It may be null if the document version of the new one is lower than one already active.
return;
}
execRunnable(moduleName, runnable);
}
}
@Override
public void visitingWillStart(IProgressMonitor monitor, boolean isFullBuild, IPythonNature nature) {
if (isFullBuild) {
AbstractAdditionalDependencyInfo info;
try {
info = AdditionalProjectInterpreterInfo.getAdditionalInfoForProject(nature);
} catch (MisconfigurationException e) {
Log.log(e);
return;
}
info.clearAllInfo();
}
}
}